package main

import (
	"context"
	"log"
	"net/http"
	"os"
	"os/signal"
	"syscall"
	"time"

	"github.com/ammyhaber/seckill/pkg/limiter"
	"github.com/ammyhaber/seckill/service/inventory/api"
	"github.com/ammyhaber/seckill/service/inventory/cache"
	"github.com/ammyhaber/seckill/service/inventory/dao"
	"github.com/ammyhaber/seckill/service/inventory/middleware"
	"github.com/ammyhaber/seckill/service/inventory/scheduler"
	"github.com/ammyhaber/seckill/service/inventory/service"
	"github.com/go-redis/redis/v8"
	"github.com/jmoiron/sqlx"
)

func main() {
	// 初始化数据库连接
	db, err := initDB()
	if err != nil {
		log.Fatalf("初始化数据库失败: %v\n", err)
	}
	defer db.Close()

	// 初始化Redis连接
	redisClient := initRedis()
	defer redisClient.Close()

	// 初始化DAO层
	inventoryDAO := dao.NewInventoryDAO(db)
	inventoryLockDAO := dao.NewInventoryLockDAO(db)

	// 初始化缓存层
	redisCache := cache.NewRedisCache(redisClient)

	// 初始化服务层
	inventoryService := service.NewInventoryService(inventoryDAO, redisCache)
	inventoryLockService := service.NewInventoryLockService(
		inventoryDAO,
		inventoryLockDAO,
		redisCache,
	)

	// 初始化库存同步服务
	syncService := service.NewInventorySyncService(inventoryService, 5*time.Minute)

	// 启动库存同步服务
	syncService.Start()
	defer syncService.Stop()

	// 初始化库存定时任务调度器
	inventoryScheduler := scheduler.NewInventoryScheduler(inventoryLockService)

	// 启动定时任务
	inventoryScheduler.Start()
	defer inventoryScheduler.Stop()

	// 初始化缓存（库存预热）
	ctx := context.Background()
	if err := inventoryService.InitCache(ctx); err != nil {
		log.Printf("库存预热失败: %v\n", err)
	} else {
		log.Println("库存预热完成")
	}

	// 初始化API层
	inventoryAPI := api.NewInventoryAPI(inventoryService)

	// 设置库存锁定服务
	inventoryAPI.SetInventoryLockService(inventoryLockService)

	// 注册HTTP路由
	mux := http.NewServeMux()
	mux.HandleFunc("/inventory", inventoryAPI.GetInventory)
	mux.HandleFunc("/inventory/decr", inventoryAPI.DecrInventory)
	mux.HandleFunc("/inventory/confirm", inventoryAPI.ConfirmDeduction)
	mux.HandleFunc("/inventory/cancel", inventoryAPI.CancelDeduction)

	// 添加库存锁定相关API
	mux.HandleFunc("/inventory/lock", inventoryAPI.LockInventory)
	mux.HandleFunc("/inventory/lock/confirm", inventoryAPI.ConfirmInventoryLock)
	mux.HandleFunc("/inventory/lock/cancel", inventoryAPI.CancelInventoryLock)

	// 创建限流器和熔断器
	// 全局限流器 - 每秒100个请求，最多积累200个令牌
	globalLimiter := limiter.NewTokenBucket(100, 200)

	// 路径级别限流器 - 针对不同接口设置不同的限流策略
	pathLimiters := map[string]limiter.RateLimiter{
		"/inventory/decr": limiter.NewTokenBucket(50, 100),  // 库存扣减接口限流较严格
		"/inventory/lock": limiter.NewTokenBucket(50, 100),  // 库存锁定接口限流较严格
		"/inventory":      limiter.NewTokenBucket(200, 300), // 查询接口限流较宽松
	}

	// 获取全局熔断器管理器
	breakerManager := middleware.GetBreakerManager()

	// 应用中间件
	var handler http.Handler = mux
	// 先应用全局限流
	handler = middleware.RateLimiterMiddleware(globalLimiter)(handler)
	// 再应用路径限流
	handler = middleware.PathRateLimiterMiddleware(pathLimiters)(handler)
	// 最后应用熔断器
	handler = middleware.CircuitBreakerMiddleware(
		breakerManager.GetBreaker("inventoryService"),
		"inventoryService",
	)(handler)

	// 启动HTTP服务
	server := &http.Server{
		Addr:    ":8080",
		Handler: handler, // 使用应用了中间件的handler
	}

	// 优雅关闭
	go func() {
		log.Println("库存服务启动，监听端口: 8080")
		if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
			log.Fatalf("HTTP服务启动失败: %v\n", err)
		}
	}()

	// 等待中断信号
	quit := make(chan os.Signal, 1)
	signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
	<-quit

	// 关闭HTTP服务
	log.Println("正在关闭服务...")
	ctxShutdown, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()

	if err := server.Shutdown(ctxShutdown); err != nil {
		log.Fatalf("服务关闭失败: %v\n", err)
	}

	log.Println("服务已关闭")
}

// initDB 初始化数据库连接
func initDB() (*sqlx.DB, error) {
	// 数据库连接信息，实际应用中应从配置文件读取
	dsn := "user:password@tcp(mysql:3306)/seckill?charset=utf8mb4&parseTime=True&loc=Local"
	db, err := sqlx.Connect("mysql", dsn)
	if err != nil {
		return nil, err
	}

	// 设置连接池参数
	db.SetMaxOpenConns(100)
	db.SetMaxIdleConns(10)
	db.SetConnMaxLifetime(time.Hour)

	return db, nil
}

// initRedis 初始化Redis连接
func initRedis() *redis.Client {
	// Redis连接信息，实际应用中应从配置文件读取
	return redis.NewClient(&redis.Options{
		Addr:     "redis:6379",
		Password: "",
		DB:       0,
		PoolSize: 100,
	})
}
